﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace yaip.Statistics {
    /// <summary>
    /// This class represents several Histograms of an image, for
    /// different channels.
    /// </summary>
    public class Histogram {

        //Arrays to save the color frequencies
        private int[] red;
        private int[] green;
        private int[] blue;
        private int[] luminance;
        //Normalized (divided by the no of pixels in the image)
        private double[] redN;
        private double[] greenN;
        private double[] blueN;
        private double[] luminanceN;


        public Histogram(Bitmap bitmap) {
            red = new int[256];
            green = new int[256];
            blue = new int[256];
            redN = new double[256];
            greenN = new double[256];
            blueN = new double[256];

            // GDI+ still lies to us - the return format is BGR, NOT RGB.
            BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int stride = bmData.Stride;
            System.IntPtr Scan0 = bmData.Scan0;

            unsafe {
                byte* p = (byte*)(void*)Scan0;
                int nOffset = stride - bitmap.Width * 3;

                for (int y = 0; y < bitmap.Height; ++y) {
                    for (int x = 0; x < bitmap.Width; ++x) {
                        //Count the frequencis of the colors in the image
                        red[p[2]]++;
                        green[p[1]]++;
                        blue[p[0]]++;

                        redN[p[2]]++;
                        greenN[p[1]]++;
                        blueN[p[0]]++;

                        //Move the pointer to the next pixel
                        p += 3;
                    }
                    p += nOffset;
                }
                //Normalize Histogram
                for (int y = 0; y < 256; y++) {
                    redN[y] = redN[y] / (bitmap.Height * bitmap.Width);
                }
            }
            bitmap.UnlockBits(bmData);
        }


        public int[] getRedHistogram() { return red; }
        public int[] getGreenHistogram() { return green; }
        public int[] getBlueHistogram() { return blue; }

        public double[] getRedNormalizedHistogram() { return redN; }
        public double[] getGreenNormalizedHistogram() { return greenN; }
        public double[] getBlueNormalizedHistogram() { return blueN; }


        //TODO: Create static class with Statistics functions!!!!


        public double getRedHistogramMean() {
            int hits;
            int total = 0;
            double mean = 0;

            // for all values
            for (int i = 0, n = red.Length; i < n; i++) {
                hits = red[i];
                // accumulate mean
                mean += i * hits;
                // accumalate total
                total += hits;
            }
            return mean / total;
        }
        public double getGreenHistogramMean() {
            int hits;
            int total = 0;
            double mean = 0;

            // for all values
            for (int i = 0, n = green.Length; i < n; i++) {
                hits = green[i];
                // accumulate mean
                mean += i * hits;
                // accumalate total
                total += hits;
            }
            return mean / total;
        }
        public double getBlueHistogramMean() {
            int hits;
            int total = 0;
            double mean = 0;

            // for all values
            for (int i = 0, n = blue.Length; i < n; i++) {
                hits = blue[i];
                // accumulate mean
                mean += i * hits;
                // accumalate total
                total += hits;
            }
            return mean / total;
        }

        public int getRedHistogramMedian() {
            int total = 0;
            int n = red.Length;

            // for all values
            for (int i = 0; i < n; i++) {
                // accumalate total
                total += red[i];
            }

            int halfTotal = total / 2;
            int median = 0;
            int v = 0;

            // find median value
            for (; median < n; median++) {
                v += red[median];
                if (v >= halfTotal)
                    break;
            }

            return median;
        }
        public int getGreenHistogramMedian() {
            int total = 0;
            int n = green.Length;

            // for all values
            for (int i = 0; i < n; i++) {
                // accumalate total
                total += green[i];
            }

            int halfTotal = total / 2;
            int median = 0;
            int v = 0;

            // find median value
            for (; median < n; median++) {
                v += green[median];
                if (v >= halfTotal)
                    break;
            }

            return median;
        }
        public int getBlueHistogramMedian() {
            int total = 0;
            int n = blue.Length;

            // for all values
            for (int i = 0; i < n; i++) {
                // accumalate total
                total += blue[i];
            }

            int halfTotal = total / 2;
            int median = 0;
            int v = 0;

            // find median value
            for (; median < n; median++) {
                v += blue[median];
                if (v >= halfTotal)
                    break;
            }

            return median;
        }
    }
}
